package cn.percent.executor;

import cn.percent.cache.CacheKey;
import cn.percent.cache.TransactionalCacheManager;
import cn.percent.config.Configuration;
import cn.percent.config.MappedStatement;
import cn.percent.utils.GenericTokenParser;
import cn.percent.utils.ParameterMappingTokenHandler;
//import org.apache.ibatis.cache.Cache;
//import org.apache.ibatis.mapping.BoundSql;
//import org.apache.ibatis.mapping.ParameterMapping;
//import org.apache.ibatis.session.ResultHandler;
//import org.apache.ibatis.session.RowBounds;

import cn.percent.config.BoundSql;
import cn.percent.sqlSession.RowBounds;
import cn.percent.utils.ParameterMapping;
//import org.apache.ibatis.cache.Cache;
import cn.percent.cache.Cache;

import java.beans.IntrospectionException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

public class CachingExecutor extends BaseExecutor{

    private Configuration configuration;
    private TransactionalCacheManager tcm = new TransactionalCacheManager();
    private Executor delegate = new SimpleExecutor();

    public CachingExecutor(Configuration configuration) {
        this.configuration = configuration;

    }

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, InvocationTargetException, IntrospectionException {
        Cache cache = configuration.getCache();
        boolean useCache = mappedStatement.getUseCache();
        System.out.println(useCache);
        Boolean fluashCache = mappedStatement.getFlushCache();
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
//        cache.putObject("a","a");
//        System.out.println(cache.getSize());
        if (cache != null) {
            if (useCache) {
                CacheKey cacheKey = createCacheKey(mappedStatement, RowBounds.DEFAULT, boundSql, params);

                List<E> list = (List<E>) tcm.getObject(cache, cacheKey);
                System.out.println(list);

                if (list == null) {
                    list = delegate.query(configuration, mappedStatement, params);
                    //cache.putObject(cacheKey,list);
                    tcm.putObject(cache,cacheKey,list);

                }
                return  list;
            }
        }

        return delegate.query(configuration,mappedStatement,params);
    }

    public CacheKey createCacheKey(MappedStatement ms,  RowBounds rowBounds, BoundSql boundSql,Object... parameterObject) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        CacheKey cacheKey = new CacheKey();
        cacheKey.update(ms.getId());
        cacheKey.update(rowBounds.getOffset());
        cacheKey.update(rowBounds.getLimit());
        cacheKey.update(boundSql.getSqlText());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappingList();
        String parameterType = ms.getParamterType();
        Class<?> parameterClassType = getClassType(parameterType);

        for (ParameterMapping parameterMapping : parameterMappings) {
            String content = parameterMapping.getContent();
            cacheKey.update(content);
            Field declaredField = parameterClassType.getDeclaredField(content);
            declaredField.setAccessible(true);
            Object o = declaredField.get(parameterObject[0]);
            cacheKey.update(o);
        }

        if (configuration.getEnvironmentId() != null) {
            cacheKey.update(configuration.getEnvironmentId());
        }
        return cacheKey;
    }

    private void flushCacheIfRequired(MappedStatement ms){
        Cache cache = ms.getCache();
        System.out.println(cache);
        if(cache != null && ms.getFlushCache()) {
            tcm.clear(cache);
        }
    }

    public void execute(Configuration configuration, MappedStatement mappedStatement,Object... params) throws ClassNotFoundException, SQLException, IllegalAccessException, NoSuchFieldException {
        flushCacheIfRequired(mappedStatement);
        delegate.execute(configuration,mappedStatement,params);
    }






}
